package su.api.mock.config;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

@Component
@Aspect
@Slf4j
public class ApiSwitchAspect implements ApplicationContextAware {

    private ApplicationContext context;
    
    // 添加缓存来存储已解析的降级方法
    private final ConcurrentHashMap<Method, Method> fallbackMethodCache = new ConcurrentHashMap<>();

    public static final Map<String, Class<? extends SwitchResolver>> MAPPINGS;
    static {
        // 初始化所有的解析器
        Map<String, Class<? extends SwitchResolver>> mappings = new HashMap<>();
        mappings.put("redis", RedisCacheResolver.class);
        MAPPINGS = Collections.unmodifiableMap(mappings);
    }

    @Pointcut("@annotation(apiSwitch)")
    private void onoff(ApiSwitch apiSwitch) {}

    @Around("onoff(apiSwitch)")
    public Object ctl(ProceedingJoinPoint pjp, ApiSwitch apiSwitch) throws Throwable {

        // 对应接口开关的key
        String key = apiSwitch.key();
        // 解析器bean的名称
        String resolverName = apiSwitch.resolver();

        SwitchResolver resolver = null;
        // 根据指定的beanName获取具体的解析器;以下都不考虑不存在的情况
        if (StringUtils.hasLength(resolverName)) {
            resolver = this.context.getBean(resolverName, SwitchResolver.class);
        } else {
            resolver = this.context.getBean(RedisCacheResolver.class);
        }

        if (resolver.resolver(key)) {
            Class<?> fallbackClass = apiSwitch.fallbackClass();
            String fallbackMethodName = apiSwitch.fallbackMethod();
            
            if (fallbackClass != void.class && StringUtils.hasLength(fallbackMethodName)) {
                try {
                    Method originalMethod = ((MethodSignature) pjp.getSignature()).getMethod();
                    Method fallbackMethod = fallbackMethodCache.computeIfAbsent(originalMethod, k -> {
                        try {
                            return fallbackClass.getMethod(fallbackMethodName, k.getParameterTypes());
                        } catch (NoSuchMethodException e) {
                            log.error("Fallback method not found or incompatible: " + fallbackMethodName, e);
                            return null;
                        }
                    });

                    if (fallbackMethod == null) {
                        return "降级方法配置错误";
                    }

                    Object fallbackInstance = context.getBean(fallbackClass);
                    return fallbackMethod.invoke(fallbackInstance, pjp.getArgs());
                } catch (Exception e) {
                    log.error("Error invoking fallback method: " + fallbackMethodName, e);
                    return "降级方法执行失败";
                }
            } else {
                return "接口不可用";
            }
        }

        return pjp.proceed();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

}